package org.springframework.security.web.access.intercept;

import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.log.LogMessage;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.matcher.RequestMatcher;

import java.util.*;

/**
 * @author Dillon
 * @date 2024/6/29
 * @slogan 致敬大师 致敬未来的你
 * @desc 默认的请求调用安全过滤角色记录实体类
 */
public class DefaultFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

	protected final Log logger = LogFactory.getLog(getClass());

	/**
	 * 不同的请求匹配器对应的权限列表Map
	 */
	private final Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;

	/**
	 * 构造函数，将请求匹配器与权限匹配注册到Map中
	 *
	 * @param requestMap 请求匹配器与权限映射MAP
	 */
	public DefaultFilterInvocationSecurityMetadataSource(
			LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap) {
		this.requestMap = requestMap;
	}

	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		Set<ConfigAttribute> allAttributes = new HashSet<>();
		this.requestMap.values().forEach(allAttributes::addAll);
		return allAttributes;
	}

	/**
	 * 获取当前请求匹配的权限列表
	 *
	 * @param object the object being secured
	 * @return 权限列表
	 */
	@Override
	public Collection<ConfigAttribute> getAttributes(Object object) {
		final HttpServletRequest request = ((FilterInvocation) object).getRequest();
		int count = 0;
		// 循环所有的请求匹配器，获取符合条件的权限列表 如果没有 返回null
		for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : this.requestMap.entrySet()) {
			if (entry.getKey().matches(request)) {
				return entry.getValue();
			} else {
				if (this.logger.isTraceEnabled()) {
					this.logger.trace(LogMessage.format("Did not match request to %s - %s (%d/%d)", entry.getKey(),
							entry.getValue(), ++count, this.requestMap.size()));
				}
			}
		}
		return null;
	}

	@Override
	public boolean supports(Class<?> clazz) {
		return FilterInvocation.class.isAssignableFrom(clazz);
	}

}
